package com.opesoft.fw.ajax;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.ClassUtils;
import org.apache.log4j.Logger;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;

public class JsonBuilder
{
	Map<String, Object> stack;

	Logger log = Logger.getLogger(this.getClass());

	public JsonBuilder()
	{
		this.stack = new HashMap<String, Object>();
	}

	public JsonBuilder(Map<String, Object> stack)
	{
		this.stack = stack;
	}

	public Object toObject(String json)
	{
		return this.toObject(json, null);
	}

	public Object getValue(String json, String property, Class<?> clazz)
	{
		try
		{
			JSONObject jsonObj = new JSONObject(json);
			return this.json2object(jsonObj.get(property), clazz);
		}
		catch(JSONException e)
		{
			log.warn(e);
		}

		return null;
	}

	public Object toObject(String json, Class<?> clazz)
	{
		try
		{
			JSONObject jsonObj = new JSONObject(json);

			if(clazz != null)
				return this.json2object(jsonObj, clazz);
			else
				return this.json2object(jsonObj);
		}
		catch(JSONException e)
		{
			log.warn(e);
			return null;
		}
	}

	/**
	 * 根据Json对象取得值
	 * 
	 * @return Object 转换后的值
	 */
	protected Object json2object(Object obj, Class<?> clazz)
	{
		// 处理为null的情况
		if(obj == null || "null".equalsIgnoreCase(obj.toString()))
			return null;
		else if(clazz != null)
		{
			if(org.springframework.util.ClassUtils.isPrimitiveOrWrapper(clazz))
				return this.json2primitive(obj, clazz);
			else if(clazz.equals(String.class))
				return obj.toString();
			else if(clazz.isArray())
				return this.json2array((JSONArray) obj, clazz);
			else if(ClassUtils.isAssignable(clazz, Collection.class))
				return this.json2collection((JSONArray) obj, clazz);
			else if(ClassUtils.isAssignable(clazz, Map.class))
				return this.json2map((JSONObject) obj, clazz);
		}

		return this.json2bean((JSONObject) obj, clazz);
	}

	protected Object json2object(Object obj)
	{
		Class<?> clazz = String.class;

		if(obj instanceof JSONObject)
		{
			try
			{
				JSONObject json = (JSONObject) obj;
				if(json.has("class"))
				{
					String s = ((JSONObject) obj).getString("class");
					if(s != null && s.length() > 0)
					{
						s = s.split("@")[0];
						clazz = Class.forName(s);
					}
				}
				else
					clazz = Map.class; //如果取不到，就把的值装在一个Map里
			}
			catch(Exception e)
			{
				log.warn(e);
			}
		}
		else if(obj instanceof JSONArray)
			clazz = Object[].class;
		else if(org.springframework.util.ClassUtils.isPrimitiveWrapper(obj.getClass()))
			clazz=ClassUtils.wrapperToPrimitive(obj.getClass());
		else if(obj.getClass().isPrimitive())
			clazz=obj.getClass();
		else
			clazz = String.class;

		return this.json2object(obj, clazz);
	}

	protected Object json2primitive(Object obj, Class<?> clazz)
	{
		if(clazz.isPrimitive())
			clazz = ClassUtils.primitiveToWrapper(clazz);

		if(clazz.equals(Boolean.class))
			return Boolean.valueOf(obj.toString());
		else if(clazz.equals(Character.class))
			return Character.valueOf(obj.toString().charAt(0));
		else if(clazz.equals(Byte.class))
			return Byte.valueOf(obj.toString());
		else if(clazz.equals(Short.class))
			return Short.valueOf(obj.toString());
		else if(clazz.equals(Integer.class))
			return Integer.valueOf(obj.toString());
		else if(clazz.equals(Long.class))
			return Long.valueOf(obj.toString());
		else if(clazz.equals(Float.class))
			return Float.valueOf(obj.toString());
		else if(clazz.equals(Double.class))
			return Double.valueOf(obj.toString());
		else
		{
			log.warn("Can't change type : " + clazz);
			return null;
		}
	}

	protected Object json2array(JSONArray obj, Class<?> clazz)
	{
		Class<?> ctype = clazz.getComponentType();

		Object r;
		if(obj == null)
			r = Array.newInstance(ctype, 0);
		else
		{
			r = Array.newInstance(ctype, obj.length());

			for(int i = 0; i < obj.length(); i++)
			{
				try
				{
					//如果类型为Object(也不能确定是什么类型)，就让不把Class传入而进行自动判断
					if(ctype.equals(Object.class))
						Array.set(r, i, this.json2object(obj.get(i)));
					else
						Array.set(r, i, this.json2object(obj.get(i), ctype));
				}
				catch(Exception e)
				{
					log.warn("Can't get index : " + i + " object from : " + obj.toString());
				}
			}
		}

		return r;
	}

	@SuppressWarnings("unchecked")
	protected Object json2collection(JSONArray obj, Class<?> clazz)
	{
		if(clazz.isInterface())
			clazz = ArrayList.class;

		Collection<Object> r = null;

		try
		{
			r = (Collection<Object>) clazz.newInstance();

			if(obj != null && obj.length() > 0)
			{
				for(int i = 0; i < obj.length(); i++)
				{
					r.add(this.json2object(obj.get(i)));
				}
			}
		}
		catch(Exception e)
		{
			log.warn(e);
		}

		return r;
	}

	@SuppressWarnings("unchecked")
	protected Object json2map(JSONObject obj, Class<?> clazz)
	{
		if(clazz.isInterface())
			clazz = HashMap.class;

		Map<Object, Object> r = null;

		try
		{
			r = (Map<Object, Object>) clazz.newInstance();

			if(obj != null)
			{
				Iterator<Object> iterator = obj.keys();

				while(iterator.hasNext())
				{
					String key = iterator.next().toString();
					r.put(key, this.json2object(obj.get(key)));
				}
			}
		}
		catch(Exception e)
		{
			log.warn(e);
		}

		return r;
	}

	protected Object json2bean(JSONObject obj, Class<?> clazz)
	{
		Object r = null;

		String className = null;
		try
		{
			className = obj.getString("class");
			if(className.indexOf('@') > -1)
			{
				r = this.stack.get(className);
				if(r != null)
					return r;
			}
		}
		catch(JSONException e)
		{
			log.warn(e);
		}

		if(clazz == null && className != null)
		{
			try
			{
				clazz = Class.forName(className.split("@")[0]);
			}
			catch(Exception e)
			{
				log.warn("Can't find class type !");
				e.printStackTrace();
				return null;
			}
		}

		try
		{
			if(clazz != null && !clazz.isInterface())
			{
				r = clazz.newInstance();
				this.stack.put(className, r);

				BeanWrapper bw = new BeanWrapperImpl(clazz);
				PropertyDescriptor[] pds = bw.getPropertyDescriptors();

				for(int i = 0; i < pds.length; i++)
				{
					try
					{
						PropertyDescriptor pd = pds[i];
						String name = pd.getName();
						if(!"class".equals(name))
						{
							Object value = this.json2object(obj.get(name), pd.getPropertyType());
							BeanUtils.setProperty(r, name, value);
						}
					}
					catch(Exception e)
					{
						log.info(e);
					}
				}
			}
			else
				log.warn("Class type : " + clazz + " can't create a instance !");
		}
		catch(Exception e)
		{
			log.warn(e);
		}

		return r;
	}

	public String toJson(Object obj)
	{
		if(obj != null)
			return this.object2json(obj).toString();
		else
			return "{}";
	}

	@SuppressWarnings("unchecked")
	protected Object object2json(Object obj)
	{
		if(obj == null)
			return JSONObject.NULL;

		Class<?> clazz = obj.getClass();
		if(org.springframework.util.ClassUtils.isPrimitiveOrWrapper(clazz))
			return obj;
		else if(String.class.equals(clazz))
			return obj;
		else if(Class.class.equals(clazz))
			return ((Class<?>) obj).getName();
		else if(clazz.isArray())
			return this.array2json(obj);
		else if(ClassUtils.isAssignable(clazz, Map.class))
			return this.map2json((Map<Object, Object>) obj);
		else if(ClassUtils.isAssignable(clazz, Collection.class))
			return this.collection2json((Collection<Object>) obj);

		return this.bean2json(obj);
	}

	protected JSONObject bean2json(Object obj)
	{
		JSONObject json = new JSONObject();

		String className = obj.getClass().getName() + "@" + obj.hashCode();

		if(this.stack.get(className) != null)
		{
			try
			{
				json.put("class", className);
			}
			catch(JSONException e)
			{
				log.warn(e);
			}
		}
		else
		{
			this.stack.put(className, obj);

			BeanWrapper bw = new BeanWrapperImpl(obj);
			PropertyDescriptor[] pds = bw.getPropertyDescriptors();

			for(int i = 0; i < pds.length; i++)
			{
				try
				{
					PropertyDescriptor pd = pds[i];
					if("class".equals(pd.getName()))
					{
						json.put("class", className);
					}
					else
						json.put(pd.getName(), this.object2json(bw.getPropertyValue(pd.getName())));
				}
				catch(Exception e)
				{
					log.warn(e);
				}
			}
		}

		return json;
	}

	protected JSONObject map2json(Map<Object, Object> map)
	{
		JSONObject json = new JSONObject();

		Object[] keys = map.keySet().toArray();

		for(int i = 0; i < keys.length; i++)
		{
			try
			{
				json.put(keys[i].toString(), this.object2json(map.get(keys[i])));
			}
			catch(JSONException e)
			{
				log.warn(e);
			}
		}

		return json;
	}

	protected JSONArray collection2json(Collection<Object> coll)
	{
		JSONArray json = new JSONArray();

		Object[] values = coll.toArray();
		for(int i = 0; i < values.length; i++)
		{
			json.put(this.object2json(values[i]));
		}

		return json;
	}

	protected JSONArray array2json(Object obj)
	{
		JSONArray json = new JSONArray();

		if(obj.getClass().isArray() && obj != null)
		{
			int len = ArrayUtils.getLength(obj);
			for(int i = 0; i < len; i++)
			{
				json.put(this.object2json(Array.get(obj, i)));
			}
		}

		return json;
	}

}
